package com.onesports.intelligent.k12.polarlight.service.order;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.onesports.framework.kit.common.util.BeanUtils;
import com.onesports.intelligent.k12.polarlight.annotation.DistributedLock;
import com.onesports.intelligent.k12.polarlight.common.errors.exceptions.BusinessException;
import com.onesports.intelligent.k12.polarlight.config.ApplicationProperties;
import com.onesports.intelligent.k12.polarlight.constant.BizConstants;
import com.onesports.intelligent.k12.polarlight.constant.CodeConstants;
import com.onesports.intelligent.k12.polarlight.constant.LockConstants;
import com.onesports.intelligent.k12.polarlight.domain.dto.pay.PayRequestDTO;
import com.onesports.intelligent.k12.polarlight.domain.entity.sys.WxPayOrder;
import com.onesports.intelligent.k12.polarlight.domain.vo.sys.NotifyVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.sys.WxPayOrderStatusVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.sys.WxPayResultVO;
import com.onesports.intelligent.k12.polarlight.mapper.sys.WxPayOrderMapper;
import com.onesports.intelligent.k12.polarlight.service.wx.WxPayOrderService;
import com.onesports.intelligent.k12.polarlight.util.CodeUtils;
import com.onesports.intelligent.k12.polarlight.util.SnowFlakeUtil;
import com.onesports.intelligent.k12.polarlight.util.WxPayUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class PayService {

    private final WxPayService wxService;

    private final PayOrderCallBackService payOrderCallBackService;

    private final ApplicationProperties properties;

    private final WxPayOrderService wxPayOrderService;

    private final CodeUtils codeUtils;


    /**
     * 微信支付下单
     *
     * @param payRequestDTO 下单请求参数
     * @return
     * @throws WxPayException
     */
    @Transactional(rollbackFor = Exception.class)
    @DistributedLock(value = LockConstants.LOCK_MARKET_ORDER_PAY, keys = "#payRequestDTO.openid")
    public WxPayResultVO createOrder(PayRequestDTO payRequestDTO) throws WxPayException {

        String appId = StringUtils.trimToNull(properties.getWxPay().getAppId());
        String mchId = StringUtils.trimToNull(properties.getWxPay().getMchId());
        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
        //订单金额
        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
        amount.setTotal(payRequestDTO.getAmount().multiply(new BigDecimal(100)).intValue());

        //是否分账
        WxPayUnifiedOrderV3Request.SettleInfo settleInfo = new WxPayUnifiedOrderV3Request.SettleInfo();
        settleInfo.setProfitSharing(payRequestDTO.getProfitSharing());

        if (payRequestDTO.getTradeType().equals(TradeTypeEnum.JSAPI)) {
            //支付者
            WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
            payer.setOpenid(payRequestDTO.getOpenid());
            request.setPayer(payer);
        }

        String outTradeNo = codeUtils.autoSerialNumber(CodeConstants.JG,4);
        request.setAmount(amount);
        request.setOutTradeNo(outTradeNo);
        request.setMchid(mchId);
        request.setAppid(appId);
        request.setNotifyUrl(properties.getWxPay().getPayNotifyUrl());
        request.setDescription(payRequestDTO.getDescription());
        request.setTimeExpire(WxPayUtil.getRfc3339(payRequestDTO.getTimeExpire().getTime()));
        request.setSettleInfo(settleInfo);
        //创建下单记录
        WxPayOrder pay = new  WxPayOrder();
//                .outTradeNo(outTradeNo)
        pay.setTradeNo(outTradeNo);
        pay.setWxUserId(payRequestDTO.getWxUserId());
        pay.setTradeState("READY");
        pay.setOrderAmount(payRequestDTO.getAmount());
        pay.setTradeType(payRequestDTO.getTradeType().toString());
        pay.setPayTime(new Date());
        pay.setPayAmount(payRequestDTO.getAmount());

        wxPayOrderService.save(pay);
        Object payResult = wxService.createOrderV3(payRequestDTO.getTradeType(),request);
        return WxPayResultVO.builder()
                .payOrderNo(outTradeNo)
                .payResult(payResult)
                .build();
    }

    /**
     * 支付结果查询
     *
     * @param payOrderNo 商户支付订单号
     * @return
     * @throws WxPayException
     */
    public WxPayOrderStatusVO queryPayStatus(String payOrderNo) throws WxPayException {

        WxPayOrder order = wxPayOrderService.getOne(new LambdaQueryWrapper<WxPayOrder>().eq(WxPayOrder::getTradeNo,payOrderNo));
        if (order == null) {
            throw new BusinessException("支付回调：未发起过支付,支付订单号："+payOrderNo);
        }

        WxPayOrderStatusVO vo = new WxPayOrderStatusVO();
        if (BizConstants.WX_PAY_SUCCESS.equals(order.getTradeState())) {
            vo.setPayStatus(true);
            vo.setMsg("已支付");
            return vo;
        }

        WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request();
        request.setMchid(properties.getWxPay().getMchId());
        request.setOutTradeNo(payOrderNo);
        WxPayOrderQueryV3Result result = wxService.queryOrderV3(request);
        paySuccess(result, order);
        vo.setPayStatus(BizConstants.WX_PAY_SUCCESS.equals(result.getTradeState()));
        vo.setMsg(result.getTradeStateDesc());
        return vo;
    }

    /**
     * 支付回调请求处理
     *
     * @param notifyData 消息
     * @param request    请求
     * @throws WxPayException
     */
    public NotifyVO payNotify(String notifyData, HttpServletRequest request) throws WxPayException, JsonProcessingException {
        NotifyVO notifyVO = new NotifyVO(BizConstants.WX_PAY_SUCCESS, "成功");
        WxPayOrderNotifyV3Result response = wxService.parseOrderNotifyV3Result(notifyData,WxPayUtil.fetchRequest2SignatureHeader(request));
        WxPayOrderNotifyV3Result.DecryptNotifyResult notifyResult = response.getResult();
        ObjectMapper objectMapper = new ObjectMapper();
        log.info("支付回调数据：" + objectMapper.writeValueAsString(notifyResult));
        String outTradeNo = notifyResult.getOutTradeNo();
        WxPayOrder order = wxPayOrderService.getOne(new LambdaQueryWrapper<WxPayOrder>().eq(WxPayOrder::getTradeNo,outTradeNo));
        if (null == order) {
            log.warn("支付回调：未发起过支付,支付订单号：" + outTradeNo);
            notifyVO.setCode("FAIL");
            return notifyVO;
        }
        if (BizConstants.WX_PAY_SUCCESS.equals(order.getTradeState())) {
            return notifyVO;
        }
        WxPayOrderQueryV3Result result = BeanUtils.copyProperties(notifyResult,WxPayOrderQueryV3Result.class);
        WxPayOrderQueryV3Result.Amount amount = BeanUtils.copyProperties(notifyResult.getAmount(),WxPayOrderQueryV3Result.Amount.class);

        if(CollectionUtils.isNotEmpty(notifyResult.getPromotionDetails())){
            List<WxPayOrderQueryV3Result.PromotionDetail> promotionDetail = notifyResult.getPromotionDetails().stream()
                    .map(item->BeanUtils.copyProperties(item,WxPayOrderQueryV3Result.PromotionDetail.class))
                    .collect(Collectors.toList());
            result.setPromotionDetails(promotionDetail);
        }
        WxPayOrderQueryV3Result.Payer payer= BeanUtils.copyProperties(notifyResult.getPayer(),WxPayOrderQueryV3Result.Payer.class);
        result.setAmount(amount);
        result.setPayer(payer);
        paySuccess(result,order);
        return notifyVO;
    }

    private void paySuccess(WxPayOrderQueryV3Result result, WxPayOrder pay){
        payOrderCallBackService.operateActivityPay(result, pay);
    }

//    /**
//     * 关闭订单
//     */
//    @Async
//    public void closePartnerTransactions(String orderId) {
//        List<MarketOrderPay> payList = payMapper.selectList(new QueryWrapper<MarketOrderPay>().lambda().eq(MarketOrderPay::getMarketOrderId, orderId));
//        if (CollectionUtils.isNotEmpty(payList)) {
//            payList.forEach(pay -> {
//                String mchId = StringUtils.trimToNull(properties.getWxPay().getMchId());
//                PartnerTransactionsCloseRequest request = new PartnerTransactionsCloseRequest();
//                request.setSpMchid(mchId);
//                request.setSubMchid(pay.getSubMchid());
//                request.setOutTradeNo(pay.getOutTradeNo());
//                try {
//                    wxService.getEcommerceService().closePartnerTransactions(request);
//                } catch (WxPayException e) {
//                    log.error("支付订单取消异常", e);
//                }
//                payMapper.deleteById(pay);
//            });
//
//        }
//    }


}
